/**
 * 
 */
package com.oron3.bouncer.objects.shapes;


/**
 * ax+by+=y
 * 
 * @author Ofek
 *
 */

public class Line implements Shape {
	public final Point p1;
	//Direction
	public final Vector d;

	/**
	 * start at p1 in the direction of p2
	 * 
	 * @param p1
	 * @param p2
	 */
	public Line(final Point p1, final Point p2) {
		this.p1 = p1;
		float a = (p2.x - p1.x);
		float b = (p2.y - p1.y);
		float c = (p2.z - p1.z);
		final float r = (float) Math.sqrt(a * a + b * b + c * c);
		a = (p2.x - p1.x) / r;
		b = (p2.y - p1.y) / r;
		c = (p2.z - p1.z) / r;
		d = new Vector(a, b, c);
	}

	/**
	 * Note : this method refers to the line as infinite
	 * 
	 * http://www.mathportal.org/analytic-geometry/analytic-geometry-3d/3d-line.php
	 * 
	 * @param p
	 * @return
	 */
	public float distanceFrom(final Point p) {
		final float det1 = (p.y - p1.y) * d.z - (p.z - p1.z) * d.y;
		final float det2 = (p.z - p1.z) * d.x - (p.x - p1.x) * d.z;
		final float det3 = (p.x - p1.x) * d.y - (p.y - p1.y) * d.x;
		return (float) Math.sqrt((det1 * det1 + det2 * det2 + det3 * det3) / (d.x * d.x + d.y * d.y + d.z * d.z));
	}

	//	public float distanceFrom2(final Point p) {
	//		final Point project = project(p);
	//		return new Vector(p.sub(project)).length();
	//	}

	@Override
	public boolean intersects(final Shape o) {
		if (o instanceof Sphere) {
			final Sphere s = ((Sphere) o);
			final float distanceFrom = distanceFrom(s.center);
			return distanceFrom <= s.r;
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oron3.bouncer.objects.GLObject#step(float)
	 */



	/**
	 * http://ocw.mit.edu/courses/mathematics/18-06sc-linear-algebra-fall-2011/least-squares-determinants-and-eigenvalues/projections-onto-subspaces/
	 * MIT18_06SCF11_Ses2.2sum.pdf
	 * 
	 * @param p
	 * @return
	 */
	public Point projectPoint(final Point p) {
		final float distanceToLine = distanceFrom(Point.ZERO);
		final float distanceToPointInLine = new Vector(p1).length();
		final float distanceToProjection = (float) Math.sqrt(distanceToPointInLine * distanceToPointInLine - distanceToLine * distanceToLine);
		final Vector zeroProjectLeft = p1.sub(d.mul(distanceToProjection)).toVector();
		final Vector zeroProjectRight = p1.add(d.mul(distanceToProjection)).toVector();
		final Vector zeroProject = zeroProjectLeft.length() < zeroProjectRight.length() ? zeroProjectLeft : zeroProjectRight;
		final Vector b = new Vector(p);
		final Vector a = d;
		final float t = a.dot(b) / a.dot(a);
		return a.mul(t).add(zeroProject);
	}


	/**
	 * http://stackoverflow.com/questions/10551555/need-an-algorithm-for-3d-vectors-intersection
	 * 
	 * @see com.oron3.bouncer.objects.shapes.Shape#intersection(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public Intersection intersection(final Shape o) {
		if (o instanceof Line) {
			final Line l = (Line) o;
			final float cosa = d.dot(l.d);
			if (cosa == 1) return null;
			final Vector vertical = new Vector(p1.add(l.p1));
			final float distance = (float) Math.sqrt(vertical.dot(vertical) * (1 - cosa * cosa));
			return new Intersection(new Vector(p1).mul(-distance));
		}
		throw new IllegalArgumentException();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "Line from P " + p1 + " in Direction " + d;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oron3.bouncer.objects.shapes.Shape#calcRelation(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public int describeRelation(final Shape o) {
		throw new IllegalArgumentException();
	}

	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.shapes.Shape#isInside(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public boolean isInside(final Shape o) {
		throw new IllegalArgumentException();
	}

	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.shapes.Shape#project(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public Shape project(final Shape o) {
		if (o instanceof Point ) {
			return projectPoint((Point)o);
		} 
		else throw new IllegalArgumentException();
	}

	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.shapes.Shape#collision(com.oron3.bouncer.objects.shapes.Shape, com.oron3.bouncer.objects.shapes.Vector)
	 */
	@Override
	public Collision collision(final Shape shape, final Vector v) {
		return null;
	}

}
